#include <kern/kmalloc.h>
#include <kern/vm.h>
#include <kern/memlayout.h>
#include <kern/debug.h>

struct kvpg_t* free_kvpgs;

static uintptr_t kvpg2kva(struct kvpg_t* kvpg)
{
	int vpn = ((int)(kvpg - kvpgs)) / sizeof *kvpg;
	return (uintptr_t)(vpn * PGSIZE + KERNBASE);
}

static struct kvpg_t* kva2kvpg(uintptr_t kva)
{
	return &kvpgs[((kva - KERNBASE) / PGSIZE)];
}

static uintptr_t alloc_kvpg(void)
{
	struct kvpg_t* kvpg;

	if (!free_kvpgs)
		return 0;

	kvpg = kvpgs;
	free_kvpgs = kvpg->next;

	return kvpg2kva(free_kva);
}

static void free_kvpg(uintptr_t kva)
{
	struct kvpg_t* kvpg;

	ASSERT(kva & (PGSIZE-1) != 0, "kva not aligned to PGSIZE");

	kvpg = kva2kvpg(kva);

	kvpg->next = free_kvpgs;
	free_kvpgs = kvpg;
}

int kmalloc_init(void)
{
	// TODO init free kvpgs
	

	return 0;
}


void* kmalloc(physaddr_t pg)
{
	uintptr_t kva;

	if (!(kva = alloc_kvpg()))
		return NULL;

	if (vm_map(kva, pa))
		goto err;

	if (vm_set_perm(kva, PTE_W))
		goto err;

	return (void*)kva;
err:
	free_kvpg(kva);
	return NULL;
}

void kfree(void* kva)
{
	ASSERT(kva & (PGSIZE-1) != 0, "kva not aligned to PGSIZE");

	vm_unmap(kva);
	free_kva((uintptr_t)kva);
}

